package com.zhou.framework.domain;

import com.zhou.util.Assert;
import lombok.extern.slf4j.Slf4j;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.io.UnsupportedEncodingException;
import java.util.*;


/**
 * windows域助手。用户获取windows域中的信息
 */
@Slf4j
public class DomainHelper {
    private LdapContext ctx = null;
    private DomainConfig cfg = null;

    private static final String INITIAL_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";

    public LdapContext connect(DomainConfig config) throws NamingException {
        if(ctx != null){
            return ctx;
        }
        Assert.notNull(config,"Domain config is not set!");

        cfg = config;
        String domainName = config.getDomainName();
        String username = config.getUsername();
        String password = config.getPassword();
        String host = config.getHost();
        String url = "ldap://" + host;
        Hashtable<String, String> env = new Hashtable<String, String>();
        // 注意用户名的写法：User@domain
        String adminName = username + "@" + domainName;
        // LDAP访问安全级别
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        // AD User
        env.put(Context.SECURITY_PRINCIPAL, adminName);
        // AD Password
        env.put(Context.SECURITY_CREDENTIALS, password);
        // LDAP工厂类
        env.put(Context.INITIAL_CONTEXT_FACTORY,  INITIAL_CONTEXT_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        //	**解决 乱码 的关键一句
        env.put("java.naming.ldap.attributes.binary","objectGUID");
        //设置连接超时时间
        env.put("com.sun.jndi.ldap.connect.timeout", "3000");
        //设置读取超时时间
        env.put("com.sun.jndi.ldap.read.timeout", "5000");
        ctx = new InitialLdapContext(env, null);
        return ctx;
    }

    public String getSearchBase(String domainOu) {
        Assert.notNull(cfg,"Domain config is not set!");
        StringBuilder searchBase = new StringBuilder();
        boolean first=true;
        if(domainOu.length()>0){
            String[] ous=domainOu.split(",");
            for(String ou : ous){
                searchBase.append("ou=").append(ou).append(",");
            }

        }
        String[] searchBases = cfg.getDomainName().split("\\.");
        for(String dc : searchBases)
        {
            if(first){
                first=false;
            }else{
                searchBase.append(",");
            }
            searchBase.append("dc=").append(dc);
        }
        return searchBase.toString();
    }

    public String getAttributeStringValue(Attributes attributes, String key) throws NamingException {
        Attribute attr = attributes.get(key);
        if (attr != null) {
            if (attr.size() > 1) {
                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < attr.size(); i++) {
                    Object obj = attr.get(i);
                    if (i != 0) {
                        sb.append(",");
                    }
                    sb.append(obj);
                }

                return sb.toString();
            } else if (attr.size() == 1) {
                Object obj = attr.get();
                if (obj != null) {
                    return obj.toString();
                } else {
                    return null;
                }
            } else {
                log.info("获取属性的size==0：" + key);
                return null;
            }
        } else {
            return null;
        }
    }
    public DomainBean searchUser(String username) throws NamingException {
        // 域节点
        String searchBase = getSearchBase("");
        String searchFilter = "(sAMAccountName=" + username + ")";
        // 搜索控制器
        SearchControls searchCtls = new SearchControls();
        // 创建搜索控制器
        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        // 定制返回属性
        String[] returnedAtts = {"name", "userPrincipalName" ,"sAMAccountName", "telephoneNumber",  "mail", "displayName", "memberOf","objectGUID" };

        searchCtls.setReturningAttributes(returnedAtts);
        // 根据设置的域节点、过滤器类和搜索控制器搜索LDAP得到结果
        NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls);
        List<DomainBean> list = getResult(answer);
        return list.size() > 0 ? list.get(0) : null;
    }
    public String getAttributeMemberOfValue(Attributes attributes, String key) throws NamingException {
        Attribute attr = attributes.get(key);
        if (attr != null) {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < attr.size(); i++) {
                Object obj = attr.get(i);
                if (i != 0) {
                    sb.append(",");
                }
                String str = obj.toString();
                int k1 = "CN=".length();
                int k2 = str.indexOf(",");
                sb.append(str.subSequence(k1, k2));
            }

            return sb.toString();
        } else {
            return null;
        }
    }
    public void close(){
        try {
            if (ctx != null) {
                ctx.close();
            }
        } catch (NamingException e) {
            log.error("close failed: ",e);
        }

    }
    public List<DomainBean> getDomainInfo() throws Exception {
        return getDomainInfo(true);
    }
    /**
     * 获得域用户信息
     * @param ifClose   是否关闭连接  true->关闭
     */
    public List<DomainBean> getDomainInfo(boolean ifClose) throws Exception {
        try
        {
            // 域节点
            String searchBase = getSearchBase("");

            // LDAP搜索过滤器类
            String searchFilter = "(&(objectCategory=person)(objectClass=user)(name=*))";
            // 搜索控制器
            SearchControls searchCtls = new SearchControls();
            // 创建搜索控制器
            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            // 定制返回属性
            String[] returnedAtts = {"name", "userPrincipalName" ,"sAMAccountName", "telephoneNumber",  "mail", "displayName", "memberOf","objectGUID" };

            searchCtls.setReturningAttributes(returnedAtts);
            // 根据设置的域节点、过滤器类和搜索控制器搜索LDAP得到结果
            NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls);

            return getResult(answer);
        } finally {
            if (ifClose) {
                close();
            }
        }
    }

    /**
     * 查询出域的组织结构
     * @param domainOu  根组织结构
     */
    public DomainOrg getOrganization(String domainOu) throws Exception {
        return getOrganization(domainOu,true);
    }
    /**
     * 获取域组织结构关系
     * @param ifClose   是否关闭连接  true->关闭
     */
    public DomainOrg getOrganization(String domainOu,boolean ifClose) throws Exception {
        DomainOrg root=null;
        try
        {
            root=new DomainOrg();
            if(ctx == null){
                throw new Error("not connect!");
            }
            // 域节点
            String searchBase = getSearchBase(domainOu);
            //ou名称 -> 中文名称
            Map<String,String> mapping=new HashMap<>();
            // LDAP搜索过滤器类top,organizationalUnit
            String searchFilter = "(&(objectCategory=CN=Organizational-Unit,CN=Schema,CN=Configuration,"+getDc()+")(objectClass=organizationalUnit)(name=*))";
            // 搜索控制器
            SearchControls searchCtls = new SearchControls();
            // 创建搜索控制器
            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            String[] returnedAtts = {"name", "distinguishedName", "description"};
            searchCtls.setReturningAttributes(returnedAtts);
            // 根据设置的域节点、过滤器类和搜索控制器搜索LDAP得到结果
            NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls);

            while (answer.hasMoreElements())
            {
                // 得到符合搜索条件的DN
                SearchResult sr = answer.next();
                Attributes attributes = sr.getAttributes();
                String name = getAttributeStringValue(attributes, "name");
                String description = getAttributeStringValue(attributes, "description");
                mapping.put(name, name);
                String ous = getAttributeStringValue(attributes, "distinguishedName");
                if(ous != null && ous.length() >0){
                        /*if(isChinese()){
                            for(String key : mapping.keySet()){
                                ous=ous.replace("OU="+key+",","OU="+mapping.get(key)+",");
                            }
                        }*/
                    //顺序取，可以简化
                    //if(isChinese()){
                    root.addObj2(ous,mapping);
                    //}else{
                    //    root.addObj1(ous);
                    //}

                }

            }
            root.setMapping(mapping);
        }  finally {
            if (ifClose) {
                close();
            }
        }
        return root;
    }
    public List<DomainBean> getDomainBeanByOU(String ou) throws Exception {
        return getDomainBeanByOU(ou,true);
    }
    /**
     * 获取ou组织下的所有用户
     * @param ou        组织名
     * @param ifClose   是否关闭连接
     */
    public List<DomainBean> getDomainBeanByOU(String ou, boolean ifClose) throws Exception {
        try
        {
            if(ctx == null){
                throw new Error("not connect!");
            }
            // 域节点
            String searchBase = ou+","+getDc();

            // LDAP搜索过滤器类
            String searchFilter = "(&(objectCategory=person)(objectClass=user)(name=*))";
            // 搜索控制器
            SearchControls searchCtls = new SearchControls();
            // 创建搜索控制器
            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            // 定制返回属性
            String[] returnedAtts = {"name", "userPrincipalName" ,"sAMAccountName", "telephoneNumber",  "mail", "displayName", "memberOf","objectGUID" };
            // 设置返回属性集
            searchCtls.setReturningAttributes(returnedAtts);
            // 根据设置的域节点、过滤器类和搜索控制器搜索LDAP得到结果
            NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls);

            return getResult(answer);
        } finally {
            if (ifClose) {
                close();
            }
        }
    }
    private List<DomainBean> getResult(NamingEnumeration<SearchResult> answer) throws NamingException {
        List<DomainBean> resultList = new ArrayList<DomainBean>();
        while (answer.hasMoreElements()) // 遍历结果集
        {
            DomainBean bean = new DomainBean();
            SearchResult sr = answer.next();// 得到符合搜索条件的DN
            Attributes attributes = sr.getAttributes();
            String name = getAttributeStringValue(attributes, "name");

            /*if ("krbtgt".equals(name)) { // 密钥发行中心服务帐户
                System.out.println("跳过账户" + name);
                continue;
            }*/

            bean.setName(name);
            bean.setPrincipalName(getAttributeStringValue(attributes, "sAMAccountName"));
            bean.setTel(getAttributeStringValue(attributes, "telephoneNumber"));
            bean.setEmail(getAttributeStringValue(attributes, "mail"));
            bean.setDisplayName(getAttributeStringValue(attributes, "displayName"));
            bean.setMemberOf(getAttributeMemberOfValue(attributes, "memberOf"));
            resultList.add(bean);
        }

        return resultList;
    }
    private String getDc(){
        if(ctx == null){
            throw new Error("not connect!");
        }
        StringBuilder res=new StringBuilder();
        String[] domain=cfg.getDomainName().split("\\.");
        for(String sec : domain){
            res.append("DC=").append(sec).append(",");
        }
        res.deleteCharAt(res.length()-1);
        return res.toString();
    }


    public static void main(String[] args) {
        DomainHelper helper = null;
        try {
            DomainConfig config = new DomainConfig();
            config.setDomainName("testad.sinitek.com");
            config.setHost("192.168.1.77");
            config.setUsername("yh.yang");
            config.setPassword("sinitek_01");
            helper = new DomainHelper();
            helper.connect(config);
            List<DomainBean> list2 = helper.getDomainInfo(false);
            List<DomainBean> list = helper.getDomainBeanByOU("ou=测试部门1,ou=组织架构",false);
            DomainOrg org = helper.getOrganization("组织架构");
    		/*for(DomainBean bean : list){
    		    if(bean.getPrincipalName() != null && bean.getPrincipalName().equals("xiong_zheng")){
    		        LOG.info(bean);
                }
            }*/
            List<String> res=new ArrayList<>();
            org.getOu(org,"基金会计",res);
            System.out.println("返回个");
            //LOG.info(JSON.toJSONString(list, true));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(helper != null){
                helper.close();
            }

        }
        System.out.println("---------结束---------");
    }

    /**
     * 解密guid
     * @param guidStr   guid
     */
    private String decodeGuid(String guidStr) {
        byte[] guidArray ;
        StringBuffer guid = new StringBuffer();
        try {
            guidArray = guidStr.getBytes("GBK");


            for (int i = 0; i < guidArray.length; i++) {
                StringBuffer dblByte = new StringBuffer(
                        Integer.toHexString(guidArray[i] & 0xff));
                if (dblByte.length() == 1) {
                    guid.append("0");
                }
                guid.append(dblByte);
            }

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return guid.toString();

    }
}
